package com.lengyu.iottransfer.gateway.influxdb;

import com.influxdb.client.InfluxDBClient;
import com.influxdb.client.QueryApi;
import com.influxdb.client.WriteApiBlocking;
import com.influxdb.client.domain.WritePrecision;
import com.influxdb.client.write.Point;
import com.influxdb.query.FluxTable;
import com.lengyu.iottransfer.gateway.influxdb.dto.QueryDataDto;
import com.lengyu.iottransfer.gateway.influxdb.dto.QueryFieldDto;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

@Service
public class InfluxdbService {
    private static final Logger logger = LoggerFactory.getLogger(InfluxdbService.class);

    @Resource
    private InfluxDBClient influxDBClient;

    @Value("${influx2.org:''}")
    private String org;

    @Value("${influx2.bucket:''}")
    private String bucket;

    public List<FluxTable> queryList(long startTime,long endTime,String tableName,int limit,String queryData,boolean descFlag) {
        List<FluxTable> tables = queryListWithGroup(startTime,endTime, bucket,tableName,null,0 ,limit, queryData, descFlag,false,null,false,true);
        return tables;
    }

    public List<FluxTable> queryList(long startTime,long endTime,String bucketName,String tableName,int limit,String queryData,boolean descFlag) {
        List<FluxTable> tables = queryListWithGroup(startTime,endTime, bucketName,tableName,null,0 ,limit, queryData, descFlag,false,null,false,true);
        return tables;
    }

    public List<FluxTable> queryList(long startTime,long endTime,String bucketName,String tableName,int offset,int limit,String queryData,boolean descFlag) {
        List<FluxTable> tables = queryListWithGroup(startTime,endTime, bucketName,tableName,null,offset ,limit, queryData, descFlag,false,null,false,true);
        return tables;
    }

    public List<FluxTable> queryListOrderByGroup(long startTime,long endTime,String bucketName,String groupByColumn,String tableName,int offset,int limit,String queryData,boolean descFlag) {
        List<FluxTable> tables = queryListWithGroup(startTime,endTime, bucketName,tableName,groupByColumn,offset ,limit, queryData, descFlag,false,null,true,false);
        return tables;
    }

    public List<FluxTable> queryListWithGroup(long startTime,long endTime,String tableName,String groupByColumn,int limit,String queryData,boolean descFlag,boolean groupFirst,String groupColumn){
        return queryListWithGroup( startTime, endTime, bucket , tableName, groupByColumn, 0,limit, queryData, descFlag, groupFirst, groupColumn,false,true);
    }

    /**
     *
     * @param startTime
     * @param endTime
     * @param bucketName
     * @param tableName
     * @param groupByColumn group列名
     * @param offset
     * @param limit
     * @param queryData
     * @param descFlag
     * @param groupFirst
     * @param groupColumn
     * @param groupListFlag group之后是否返回list
     * @param pivotFlag   不同field是否合并为一条数据
     * @return
     */
    public List<FluxTable> queryListWithGroup(long startTime,long endTime,String bucketName,String tableName
            ,String groupByColumn,int offset,int limit,String queryData,boolean descFlag,boolean groupFirst
            ,String groupColumn,boolean groupListFlag,boolean pivotFlag) {
        StringBuilder fluxSb = new StringBuilder();
        fluxSb.append("from(bucket:\"").append(bucketName).append("\")\n");
        fluxSb.append(" |> range(start:").append(startTime/1000).append(", stop:").append(endTime/1000).append(")\n");
        fluxSb.append(" |> filter(fn: (r) => r[\"_measurement\"] == \"").append(tableName).append("\")\n");


        if(StringUtils.isNotBlank(groupByColumn)){
            fluxSb.append("|> group(columns: [\"").append(groupByColumn).append("\"])");
            if(!groupListFlag){
                if(groupFirst){
                    fluxSb.append("|> first(column: \"").append(groupColumn).append("\")");
                }
                else{
                    fluxSb.append("|> last(column: \"").append(groupColumn).append("\")");
                }
            }
            else{
                fluxSb.append("|> sort(columns: [\"_time\"],desc: ").append(descFlag).append(" )");
                if(limit > 0){
                    fluxSb.append("|> limit(n:").append(limit).append(", offset:").append(offset).append(")\n");
                }
            }
        }
        else{
            fluxSb.append("|> sort(columns: [\"_time\"],desc: ").append(descFlag).append(" )");
            if(limit > 0){
                fluxSb.append("|> limit(n:").append(limit).append(", offset:").append(offset).append(")\n");
            }
        }
        if(pivotFlag){
            fluxSb.append("|> pivot(\n" +
                    "    rowKey:[\"_time\"],\n" +
                    "    columnKey: [\"_field\"],\n" +
                    "    valueColumn: \"_value\"\n" +
                    "  )");
        }

//        //东八区
//        fluxSb.append("|> timeShift(duration: 8h)");
        if(StringUtils.isNotBlank(queryData)){
            fluxSb.append(queryData);
        }
        QueryApi queryApi = influxDBClient.getQueryApi();
        logger.info("查询语句：" + fluxSb);
        List<FluxTable> tables = queryApi.query(fluxSb.toString(), org);
        String result  = queryApi.queryRaw(fluxSb.toString(),org);
        logger.info("tables:{}", result);
        return tables;
    }

    public long totalCount(long startTime,long endTime,String bucketName,String tableName,
            String queryData,boolean descFlag){
        long totalCount = 0;
        StringBuilder fluxSb = new StringBuilder();
        fluxSb.append("from(bucket:\"").append(bucketName).append("\")\n");
        fluxSb.append(" |> range(start:").append(startTime/1000).append(", stop:").append(endTime/1000).append(")\n");
        fluxSb.append(" |> filter(fn: (r) => r[\"_measurement\"] == \"").append(tableName).append("\")\n");

        fluxSb.append("|> count()");
//        //东八区
//        fluxSb.append("|> timeShift(duration: 8h)");
        if(StringUtils.isNotBlank(queryData)){
            fluxSb.append(queryData);
        }
        QueryApi queryApi = influxDBClient.getQueryApi();
        logger.info("查询语句：" + fluxSb);
        List<FluxTable> tables = queryApi.query(fluxSb.toString(), org);
        String result  = queryApi.queryRaw(fluxSb.toString(),org);
        logger.info("tables:{}", result);
        if(tables != null && tables.size() > 0) {

            for (FluxTable table : tables) {
                totalCount += Long.parseLong(String.valueOf(table.getRecords().get(0).getValue()));
            }

        }
        return  totalCount;
     }

    public String queryParamStr(String compare,Object value,String key){
        StringBuilder sb = new StringBuilder();
        if(compare.equals("=")){
            sb.append(" |> filter(fn: (r) => r[\"").append(key).append("\"] == \"").append(value).append("\")\n");
        }
        if(compare.equals("in")){
            if(value instanceof ArrayList){
                List<String> valueList =(ArrayList)value;
                if(valueList != null && valueList.size() > 0){
                    sb.append(" |> filter(fn: (r) => ");

                    for (String s : valueList) {
                        sb.append("r[\"").append(key).append("\"] == \"").append(s).append("\"").append(" or ");
                    }
                    if(sb.toString().endsWith("or ")){
                        sb.delete(sb.length() - 3,sb.length() -1);
                    }
                    sb.append(")\n");
                }
       
            }

        }

        return sb.toString();
    }

    public String queryFieldStr(List<String> fieldList){
        if(fieldList != null && fieldList.size() > 0){
            StringBuilder sb = new StringBuilder();
            sb.append("|> filter(fn: (r) => ");
            fieldList.forEach(i->{
                sb.append("r[\"_field\"] == \"").append(i).append("\"");
                sb.append("or");
            });
            sb.delete(sb.length()-2,sb.length());
            sb.append(")\n");
            return sb.toString();
        }
        return null;
    }

    /**
     * @param entity 表名
     */
    public void save(QueryDataDto entity,long time,int nanoSecond) {
        WriteApiBlocking writeApiBlocking = influxDBClient.getWriteApiBlocking();
        Point point = convertEntityToPoint(entity,time,nanoSecond);

        writeApiBlocking.writePoint(bucket, org, point);
    }


    /**
     * @param entity 表名
     */
    public void save(QueryDataDto entity,String bucket,long time,int nanoSecond) {
        WriteApiBlocking writeApiBlocking = influxDBClient.getWriteApiBlocking();
        Point point = convertEntityToPoint(entity,time,nanoSecond);

        writeApiBlocking.writePoint(bucket, org, point);
    }

    /**
     * @param
     */
    public void batchSave(List<QueryDataDto> entityList, long time, int nanoSecond) {
        WriteApiBlocking writeApiBlocking = influxDBClient.getWriteApiBlocking();
        List<Point> pointList = new ArrayList<>();

        for (QueryDataDto entity: entityList) {
            Point point = convertEntityToPoint(entity,time,nanoSecond);
            pointList.add(point);
        }

        writeApiBlocking.writePoints(bucket,org,pointList);
    }

    private void setValueByValueType(Point point , QueryFieldDto fieldDto){
        String valueType = fieldDto.getValueType();
        if(valueType.equals("boolean")){
            point.addField(fieldDto.getKey().toLowerCase(),(Boolean)fieldDto.getValue());
        }
        else if(valueType.equals("long")){
            point.addField(fieldDto.getKey().toLowerCase(),(Long)fieldDto.getValue());
        }
        else if(valueType.equals("string")){
            if(fieldDto.isTagFlag()){
                point.addTag(fieldDto.getKey().toLowerCase(),String.valueOf(fieldDto.getValue()));
            }
            else{
                point.addField(fieldDto.getKey().toLowerCase(),String.valueOf(fieldDto.getValue()));
            }
        }
        else if(valueType.equals("double") || valueType.equals("float")){
            if(fieldDto.getValue() instanceof BigDecimal){
                point.addField(fieldDto.getKey().toLowerCase(),((BigDecimal)fieldDto.getValue()).doubleValue());
            }
            else if(fieldDto.getValue() instanceof Float){
                point.addField(fieldDto.getKey().toLowerCase(),((Float)fieldDto.getValue()).doubleValue());
            }
            else{
                point.addField(fieldDto.getKey().toLowerCase(),(Double)fieldDto.getValue());
            }

        }
        else if(valueType.equals("int")){
            point.addField(fieldDto.getKey().toLowerCase(),(Integer)fieldDto.getValue());
        }
        else if(valueType.equals("bigDecimal")){
            point.addField(fieldDto.getKey().toLowerCase(),((BigDecimal)fieldDto.getValue()).doubleValue());
        }

    }

    private Point convertEntityToPoint(QueryDataDto entity,long time,int nanoSecond){
        //表明
        Point point = Point.measurement(entity.getTableName());

        //字段信息
        for (QueryFieldDto field: entity.getFieldDtoList()) {
            if(field.getKey().equals("time")){
                time = (Long)(field.getValue());
            }
            else if(field.getKey().equals("deviceId")){
                point = point.addTag(field.getKey(),String.valueOf(field.getValue()));
            }
            else{
                setValueByValueType(point,field);
            }
        }
        point = point.time(Instant.ofEpochSecond(time/1000,nanoSecond),WritePrecision.NS);
        return point;
    }


}
